package it.unipd.fbaron.mind_reader_squared.activities;

import it.unipd.fbaron.mind_reader_squared.R;
import it.unipd.fbaron.mind_reader_squared.model.DataBaseHelper;
import it.unipd.fbaron.mind_reader_squared.model.ProfileLogic;
import it.unipd.fbaron.mind_reader_squared.model.SymbolDAO;
import it.unipd.fbaron.mind_reader_squared.model.SymbolLogic;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.AssetFileDescriptor;
import android.graphics.Color;
import android.graphics.Typeface;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.TextSwitcher;
import android.widget.TextView;
import android.widget.ViewSwitcher.ViewFactory;

public class M1Activity extends BaseActivity {

	// TextSwitcher
	private TextSwitcher textSwitcher;

	// Gallery
	private M1Gallery gallery;
	private SymbolAdapter galleryAdapter;

	// Sound
	private MediaPlayer soundEffectsPlayer;
	private AssetFileDescriptor buttonSoundFd;
	private AssetFileDescriptor slideSoundFd;

	// Input variables
	private int leftInputMode;
	private int rightInputMode;
	private int selectInputMode;

	// TouchListener variables
	private float lastDownActionX = 0;
	private long lastDownActionTime = 0;
	
	//EPOC Broadcast receivers
	private BroadcastReceiver br_left, br_right, br_up, br_down;

	@Override
	public void onDestroy() {
		super.onDestroy();

		if (soundEffectsPlayer != null) {
			soundEffectsPlayer.release();
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.m1_layout);

		// Get input modes
		String inputModes[] = ProfileLogic.getInstance(dbHelper).queryWithId(
				profileId,
				new String[] { "input_left", "input_right", "input_select" });
		leftInputMode = Integer.parseInt(inputModes[0]);
		rightInputMode = Integer.parseInt(inputModes[1]);
		selectInputMode = Integer.parseInt(inputModes[2]);

		//Create EPOC broadcast receivers, they are registered in onResume
		br_left = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
		      	doAction(DataBaseHelper.INPUT_EPOC_LEFT);
			}
		};
		br_right = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
		      	doAction(DataBaseHelper.INPUT_EPOC_RIGHT);
			}
		};
		br_up = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
		      	doAction(DataBaseHelper.INPUT_EPOC_UP);
			}
		};
		br_down = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
		      	doAction(DataBaseHelper.INPUT_EPOC_DOWN);
			}
		};
		
		// Prepare soundEffectsPlayer to reproduce sounds stored in application
		// resources
		try {
			buttonSoundFd = getAssets().openFd("button.wav");
			slideSoundFd = getAssets().openFd("image_slide.wav");
		} catch (IOException e) {}
		soundEffectsPlayer = new MediaPlayer();
		soundEffectsPlayer.setOnPreparedListener(new OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {
				mp.start();
			}
		});
		soundEffectsPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
				mp.stop();
				mp.reset();
			}
		});

		// Setting Gallery
		gallery = (M1Gallery) findViewById(R.id.gallery);
		galleryAdapter = new SymbolAdapter(this);
		gallery.setAdapter(galleryAdapter);
		// Event listeners must be defined because Activity's event listener
		// doesn't receive Gallery's callbacks so we call'em explicitly
		gallery.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				return M1Activity.this.onTouchEvent(event);
			}
		});
		gallery.setOnKeyListener(new OnKeyListener() {

			@Override
			public boolean onKey(View v, int keyCode, KeyEvent event) {
				if (event.getAction() == KeyEvent.ACTION_DOWN)
					return M1Activity.this.onKeyDown(keyCode, event);
				else if (event.getAction() == KeyEvent.ACTION_UP)
					return M1Activity.this.onKeyUp(keyCode, event);
				else if (event.getAction() == KeyEvent.FLAG_LONG_PRESS)
					return M1Activity.this.onKeyLongPress(keyCode, event);
				return false;
			}
		});
		gallery.setOnItemSelectedListener(new OnItemSelectedListener() {

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				String currText = galleryAdapter.getText(arg2);
				textSwitcher.setText(currText.toUpperCase());

				try {
					soundEffectsPlayer.setDataSource(
							slideSoundFd.getFileDescriptor(),
							slideSoundFd.getStartOffset(),
							slideSoundFd.getLength());
					soundEffectsPlayer.prepareAsync();
				} catch (Exception e) {
					e.printStackTrace();
				}
				if (readAll) {
					SymbolDAO symbol = (SymbolDAO) galleryAdapter
							.getItem(gallery.getSelectedItemPosition());
					if (symbol.getSoundChoise() > 0)
						startSymbolSound(symbol.getSoundChoise(),
								symbol.getSoundPath());
					else
						startSymbolSound(symbol.getSoundChoise(),
								symbol.getText());
				}
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
			}
		});

		// Setting TextSwitcher
		textSwitcher = (TextSwitcher) findViewById(R.id.textSwitcher);
		textSwitcher.setFactory(new ViewFactory() {
			@Override
			public View makeView() {
				TextView textView = new TextView(getApplicationContext());
				textView.setGravity(Gravity.CENTER_HORIZONTAL);
				textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
				textView.setTypeface(Typeface.create("default", Typeface.BOLD));
				textView.setTextColor(Color.BLACK);
				textView.setBackgroundResource(R.drawable.text_frame_default);
				textView.setLayoutParams(new ImageSwitcher.LayoutParams(
						LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
				return textView;
			}
		});
		textSwitcher.setAnimateFirstView(true);
		textSwitcher.setText(galleryAdapter.getText(
				gallery.getFirstVisiblePosition()).toUpperCase());

		if (readAll) {
			SymbolDAO symbol = (SymbolDAO) galleryAdapter.getItem(gallery
					.getSelectedItemPosition());
			if (symbol.getSoundChoise() > 0)
				startSymbolSound(symbol.getSoundChoise(), symbol.getSoundPath());
			else
				startSymbolSound(symbol.getSoundChoise(), symbol.getText());
		}
		
	}

	@Override
	public void onPause() {
		super.onDestroy();

		if (soundEffectsPlayer != null) {
			soundEffectsPlayer.release();
			soundEffectsPlayer = null;
		}
		
		unregisterReceiver(br_left);
		unregisterReceiver(br_right);
		unregisterReceiver(br_up);
		unregisterReceiver(br_down);
	}

	@Override
	public void onResume() {
		super.onResume();

		soundEffectsPlayer = new MediaPlayer();
		soundEffectsPlayer.setOnPreparedListener(new OnPreparedListener() {

			@Override
			public void onPrepared(MediaPlayer mp) {
				mp.start();
			}
		});
		soundEffectsPlayer.setOnCompletionListener(new OnCompletionListener() {

			@Override
			public void onCompletion(MediaPlayer mp) {
				mp.stop();
				mp.reset();
			}
		});
		
		//Set EPOC broadcast receivers
        IntentFilter intentFilter1 = new IntentFilter("vocatio.ACTION_LEFT");
        registerReceiver(br_left, intentFilter1);
        IntentFilter intentFilter2 = new IntentFilter("vocatio.ACTION_RIGHT");
        registerReceiver(br_right, intentFilter2);
        IntentFilter intentFilter3 = new IntentFilter("vocatio.ACTION_UP");
        registerReceiver(br_up, intentFilter3);
        IntentFilter intentFilter4 = new IntentFilter("vocatio.ACTION_DOWN");
        registerReceiver(br_down, intentFilter4);
	}

	/*
	 * Move to next image of the gallery
	 */
	private void viewNextImage() {
		int nextPosition = gallery.getSelectedItemPosition() + 1;
		if (nextPosition >= gallery.getCount())
			nextPosition = 0;
		gallery.setSelection(nextPosition, true);
	}

	/*
	 * Move to previous image of the gallery
	 */
	private void viewPrevImage() {
		int prevPosition = gallery.getSelectedItemPosition() - 1;
		if (prevPosition < 0)
			prevPosition = gallery.getCount() - 1;
		gallery.setSelection(prevPosition, true);
	}

	/*
	 * Select an image updating db statistics.
	 */
	private void select() {
		try {
			soundEffectsPlayer.setDataSource(buttonSoundFd.getFileDescriptor(),
					buttonSoundFd.getStartOffset(), buttonSoundFd.getLength());
			soundEffectsPlayer.prepareAsync();
		} catch (Exception e) {
			e.printStackTrace();
		}
		SymbolDAO symbol = (SymbolDAO) galleryAdapter.getItem(gallery
				.getSelectedItemPosition());
		if (symbol.getSoundChoise() > 0)
			startSymbolSound(symbol.getSoundChoise(), symbol.getSoundPath());
		else
			startSymbolSound(symbol.getSoundChoise(), symbol.getText());

		SymbolLogic.getInstance(dbHelper).saveClicks(
				(int) galleryAdapter.getItemId(gallery
						.getSelectedItemPosition()), 1, true);
	}

	public int getProfileId() {
		return profileId;
	}

	/*
	 * GALLERY INNER CLASS The only customization are the blank overrides to
	 * remove standard management of events by Gallery class.
	 */
	public static class M1Gallery extends Gallery {

		public M1Gallery(Context context, AttributeSet attrs) {
			super(context, attrs);
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			// Blank to remove fling events
			return true;
		}

		@Override
		public boolean onKeyDown(int keyCode, KeyEvent event) {
			// Blank to remove default KeyDown events
			return false;
		}

		@Override
		public boolean onKeyUp(int keyCode, KeyEvent event) {
			// Blank to remove default KeyUp events
			return false;
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			// Blank to remove default SungleTapUp events
			return false;
		}

		@Override
		public void onLongPress(MotionEvent e) {
			// Blank to remove default LongPress events
		}

		@Override
		public void onShowPress(MotionEvent e) {
			// Blank to remove default ShowPress events
		}

	}

	/*
	 * SYMBOL ADAPTER INNER CLASS Endless adapter that repeat symbol list when
	 * arrived to the end.
	 */
	public class SymbolAdapter extends BaseAdapter {
		private Context mContext;
		private ArrayList<SymbolDAO> symbols;

		public SymbolAdapter(Context c) {
			mContext = c;
			symbols = SymbolLogic.getInstance(dbHelper).getM1SymbolList(
					profileId);
		}

		@Override
		public int getCount() {
			return Integer.MAX_VALUE;
		}

		@Override
		public Object getItem(int position) {
			if (position >= symbols.size()) {
				position = position % symbols.size();
			}
			return symbols.get(position);
		}

		public String getText(int position) {
			if (position >= symbols.size()) {
				position = position % symbols.size();
			}
			return symbols.get(position).getText();
		}

		@Override
		public long getItemId(int position) {
			if (position >= symbols.size()) {
				position = position % symbols.size();
			}
			return symbols.get(position).getId();
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			if (position >= symbols.size()) {
				position = position % symbols.size();
			}

			ImageView imageView = new ImageView(mContext);

			imageView.setImageDrawable(getSymbolImage(symbols.get(position)
					.getImageResource(), symbols.get(position).getImagePath()));
			imageView.setLayoutParams(new Gallery.LayoutParams(
					LayoutParams.FILL_PARENT,
					LayoutParams.FILL_PARENT));
			imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
			imageView.setBackgroundResource(R.drawable.picture_frame_default);

			return imageView;
		}
	}

	/************************************
	 ********** EVENT MANAGERS **********
	 ***********************************/

	/*
	 * do an action corresponding to input setting for the user.
	 */
	private boolean doAction(int input_type) {
		boolean result = false;

		if (leftInputMode == input_type) {
			viewNextImage();
			result = true;
		}
		if (rightInputMode == input_type) {
			viewPrevImage();
			result = true;
		}
		if (selectInputMode == input_type) {
			select();
			result = true;
		}
		return result;
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_SPACE) {
			if (leftInputMode == DataBaseHelper.INPUT_KEY_LONG_CLICK
					|| rightInputMode == DataBaseHelper.INPUT_KEY_LONG_CLICK
					|| selectInputMode == DataBaseHelper.INPUT_KEY_LONG_CLICK) {
				event.startTracking();
			}
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyLongPress(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_SPACE) {
			return true;
		}
		return super.onKeyLongPress(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		int key = event.getKeyCode();

		// disable back event from other devices (es. mouse right click)
		if (key == KeyEvent.KEYCODE_BACK && event.getDeviceId() > 0) {
			return true;
		}

		// key long click
		if (keyCode == KeyEvent.KEYCODE_SPACE && event.isTracking()
				&& event.isCanceled()) {
			if (doAction(DataBaseHelper.INPUT_KEY_LONG_CLICK))
				return true;
		}

		// key click
		if (doAction(DataBaseHelper.INPUT_KEY_CLICK))
			return true;

		switch (key) {
		// key location left
		case KeyEvent.KEYCODE_CTRL_LEFT:
		case KeyEvent.KEYCODE_FUNCTION:
		case KeyEvent.KEYCODE_HOME:
		case KeyEvent.KEYCODE_BACKSLASH:
		case KeyEvent.KEYCODE_ALT_LEFT:
		case KeyEvent.KEYCODE_SHIFT_LEFT:
		case KeyEvent.KEYCODE_CAPS_LOCK:
		case KeyEvent.KEYCODE_TAB:
		case KeyEvent.KEYCODE_ESCAPE:
		case KeyEvent.KEYCODE_1:
		case KeyEvent.KEYCODE_2:
		case KeyEvent.KEYCODE_3:
		case KeyEvent.KEYCODE_4:
		case KeyEvent.KEYCODE_5:
		case KeyEvent.KEYCODE_6:
		case KeyEvent.KEYCODE_F1:
		case KeyEvent.KEYCODE_F2:
		case KeyEvent.KEYCODE_F3:
		case KeyEvent.KEYCODE_F4:
		case KeyEvent.KEYCODE_F5:
		case KeyEvent.KEYCODE_F6:
		case KeyEvent.KEYCODE_Z:
		case KeyEvent.KEYCODE_X:
		case KeyEvent.KEYCODE_C:
		case KeyEvent.KEYCODE_V:
		case KeyEvent.KEYCODE_A:
		case KeyEvent.KEYCODE_S:
		case KeyEvent.KEYCODE_D:
		case KeyEvent.KEYCODE_F:
		case KeyEvent.KEYCODE_G:
		case KeyEvent.KEYCODE_Q:
		case KeyEvent.KEYCODE_W:
		case KeyEvent.KEYCODE_E:
		case KeyEvent.KEYCODE_R:
		case KeyEvent.KEYCODE_T:
			if (doAction(DataBaseHelper.INPUT_KEY_LOCATION_LEFT_CLICK))
				return true;
			break;

		// key location right
		case KeyEvent.KEYCODE_NUMPAD_0:
		case KeyEvent.KEYCODE_NUMPAD_1:
		case KeyEvent.KEYCODE_NUMPAD_2:
		case KeyEvent.KEYCODE_NUMPAD_3:
		case KeyEvent.KEYCODE_NUMPAD_4:
		case KeyEvent.KEYCODE_NUMPAD_5:
		case KeyEvent.KEYCODE_NUMPAD_6:
		case KeyEvent.KEYCODE_NUMPAD_7:
		case KeyEvent.KEYCODE_NUMPAD_8:
		case KeyEvent.KEYCODE_NUMPAD_9:
		case KeyEvent.KEYCODE_NUM_LOCK:
		case KeyEvent.KEYCODE_NUMPAD_ADD:
		case KeyEvent.KEYCODE_NUMPAD_COMMA:
		case KeyEvent.KEYCODE_NUMPAD_DIVIDE:
		case KeyEvent.KEYCODE_NUMPAD_DOT:
		case KeyEvent.KEYCODE_NUMPAD_ENTER:
		case KeyEvent.KEYCODE_NUMPAD_EQUALS:
		case KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN:
		case KeyEvent.KEYCODE_NUMPAD_MULTIPLY:
		case KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN:
		case KeyEvent.KEYCODE_NUMPAD_SUBTRACT:
		case KeyEvent.KEYCODE_ALT_RIGHT:
		case KeyEvent.KEYCODE_CTRL_RIGHT:
		case KeyEvent.KEYCODE_DPAD_CENTER:
		case KeyEvent.KEYCODE_DPAD_DOWN:
		case KeyEvent.KEYCODE_DPAD_LEFT:
		case KeyEvent.KEYCODE_DPAD_RIGHT:
		case KeyEvent.KEYCODE_DPAD_UP:
		case KeyEvent.KEYCODE_DEL:
		case KeyEvent.KEYCODE_SHIFT_RIGHT:
		case KeyEvent.KEYCODE_ENDCALL:
		case KeyEvent.KEYCODE_PAGE_UP:
		case KeyEvent.KEYCODE_PAGE_DOWN:
		case KeyEvent.KEYCODE_INSERT:
		case KeyEvent.KEYCODE_F12:
		case KeyEvent.KEYCODE_F11:
		case KeyEvent.KEYCODE_F10:
		case KeyEvent.KEYCODE_F9:
		case KeyEvent.KEYCODE_F8:
		case KeyEvent.KEYCODE_8:
		case KeyEvent.KEYCODE_9:
		case KeyEvent.KEYCODE_0:
		case KeyEvent.KEYCODE_APOSTROPHE:
		case KeyEvent.KEYCODE_BACK:
		case KeyEvent.KEYCODE_PLUS:
		case KeyEvent.KEYCODE_COMMA:
		case KeyEvent.KEYCODE_P:
		case KeyEvent.KEYCODE_O:
		case KeyEvent.KEYCODE_I:
		case KeyEvent.KEYCODE_U:
		case KeyEvent.KEYCODE_L:
		case KeyEvent.KEYCODE_K:
		case KeyEvent.KEYCODE_J:
		case KeyEvent.KEYCODE_M:
		case KeyEvent.KEYCODE_N:
			if (doAction(DataBaseHelper.INPUT_KEY_LOCATION_RIGHT_CLICK))
				return true;
			break;
		}

		// key space
		if (key == KeyEvent.KEYCODE_SPACE) {
			if (doAction(DataBaseHelper.INPUT_KEY_SPACE_CLICK))
				return true;
		}

		// key left
		if (key == KeyEvent.KEYCODE_DPAD_LEFT) {
			if (doAction(DataBaseHelper.INPUT_KEY_LEFT_CLICK))
				return true;
		}

		// key right
		if (key == KeyEvent.KEYCODE_DPAD_RIGHT) {
			if (doAction(DataBaseHelper.INPUT_KEY_RIGHT_CLICK))
				return true;
		}

		// key up
		if (key == KeyEvent.KEYCODE_DPAD_UP) {
			if (doAction(DataBaseHelper.INPUT_KEY_UP_CLICK))
				return true;
		}

		// key down
		if (key == KeyEvent.KEYCODE_DPAD_DOWN) {
			if (doAction(DataBaseHelper.INPUT_KEY_DOWN_CLICK))
				return true;
		}

		// key enter
		if (key == KeyEvent.KEYCODE_DPAD_CENTER
				|| key == KeyEvent.KEYCODE_ENTER) {
			if (doAction(DataBaseHelper.INPUT_KEY_ENTER_CLICK))
				return true;
		}

		return super.onKeyUp(keyCode, event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		if (event.getAction() == MotionEvent.ACTION_DOWN) {
			lastDownActionX = event.getX();
			lastDownActionTime = new Date().getTime();
			return false;
		}

		if (event.getAction() == MotionEvent.ACTION_UP) {

			// mouse long click
			if (new Date().getTime() - lastDownActionTime > 1000) {
				if (doAction(DataBaseHelper.INPUT_MOUSE_LONG_CLICK))
					return true;
			}

			// mouse click
			if (event.getX() - lastDownActionX > -100
					&& event.getX() - lastDownActionX < 100) {
				if (doAction(DataBaseHelper.INPUT_MOUSE_CLICK))
					return true;
			}
		}
		return false;
	}
}